from datetime import datetime, timedelta
import platform

from typing import Optional

import cv2
import numpy as np
from fastapi import HTTPException
from jose import jwt
from passlib.context import CryptContext
from pymilvus import MilvusClient

from src.utils.logging import log
from config import settings

platform_name = platform.platform().lower()
# 安全配置
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT配置
SECRET_KEY = settings.SECRET_KEY
ALGORITHM = settings.ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = int(settings.ACCESS_TOKEN_EXPIRE_MINUTES)

# Milvus配置
milvus_host = settings.MILVUS_HOST
milvus_port = settings.MILVUS_PORT
milvus_uri = f"http://{milvus_host}:{milvus_port}"
collection_name = settings.FACE_COLLECTION_NAME
milvus = None
if "windows" in platform_name:
    # 初始化Milvus客户端
    milvus = MilvusClient(
        uri=milvus_uri,
        token="root:Milvus"
    )
elif "ubuntu" in platform_name or "debian" or "centos" in platform_name:
    # 初始化Milvus客户端
    milvus = MilvusClient("./data/milvus.db")

milvus.create_collection(
    collection_name=collection_name,
    dimension=1024
)


def verify_password(plain_password: str, hashed_password: str):
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password: str):
    return pwd_context.hash(password)


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now() + expires_delta
    else:
        expire = datetime.now() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def save_face_vector(user_id: int, features):
    try:
        if not isinstance(features, np.ndarray):
            features_np = np.array(features, dtype=np.float32)
        else:
            features_np = features

        feature_list = features_np.tolist()

        res = milvus.upsert(
            collection_name=collection_name,
            data={"id": user_id, "vector": feature_list}
        )
        log.info(f"Inserted vector for user {user_id}")
        return res
    except Exception as e:
        log.error(f"Failed to insert vector: {str(e)}")
        raise HTTPException(status_code=500, detail="Vector insertion failed")


def search_face_vector(features, top_k: int = 5, threshold: float = 0.7):
    try:
        if not isinstance(features, np.ndarray):
            features_np = np.array(features, dtype=np.float32)
        else:
            features_np = features
        query_vector = features_np.tolist()
        search_params = {
            "metric_type": "COSINE",  # 相似度计算方式
            "params": {
                "nprobe": 10,  # 搜索的聚类中心数量，影响精度和速度
            }
        }

        results = milvus.search(
            collection_name=collection_name,
            data=[query_vector],
            anns_field="vector",  # 指定向量字段名
            limit=top_k,
            search_params=search_params,
            output_fields=["id", "vector", "distance"]
        )

        matches = []
        for hits in results:
            for hit in hits:
                if hit["distance"] >= threshold:
                    matches.append((hit["id"], hit["distance"], hit["vector"]))

        return matches
    except Exception as e:
        log.error(f"Search failed: {str(e)}")
        raise HTTPException(status_code=500, detail="Vector search failed")


def delete_face_vector(user_id: int):
    try:
        milvus.delete(
            collection_name=collection_name,
            ids=[user_id]
        )
        log.info(f"Deleted vector for user {user_id}")
    except Exception as e:
        log.error(f"Deletion failed: {str(e)}")
        raise HTTPException(status_code=500, detail="Vector deletion failed")


def save_face_image(user_id: int, image_data: bytes) -> str:
    """同步保存图片，确保能立即返回URL"""
    try:
        settings.FACE_IMAGE_DIR.mkdir(exist_ok=True)
        filename = f"{user_id}.jpg"
        filepath = settings.FACE_IMAGE_DIR / filename

        # 直接保存二进制数据（比OpenCV解码+保存更快）
        with open(filepath, "wb") as f:
            f.write(image_data)

        return f"/static/face_images/{filename}"
    except Exception as e:
        log.error(f"Failed to save face image: {str(e)}")
        raise HTTPException(status_code=500, detail="Failed to save face image")


def save_results_face_image(user_id: int, image_data) -> str:
    """同步保存识别完成的结果图片，确保能立即返回URL"""
    try:
        settings.FACE_RESULTS_IMAGE_DIR.mkdir(exist_ok=True)
        filename = f"{user_id}.jpg"
        filepath = settings.FACE_RESULTS_IMAGE_DIR / filename

        cv2.imwrite(str(filepath), image_data)

        return f"/static/face_results_images/{filename}"
    except Exception as e:
        log.error(f"Failed to save face image: {str(e)}")
        raise HTTPException(status_code=500, detail="Failed to save face image")
